﻿#region References

using System;
using System.Collections.Generic;
using App_Code.Objects;
using Models;
using System.Linq;
using ConnectionProfile = Models.ConnectionProfile;
using UserProfile = App_Code.Objects.UserProfile;

#endregion

namespace App_Code.Logic
{
    /// <summary>
    /// Summary description for UserProfileLogic
    /// </summary>
    public static class UserProfileLogic
    {
        #region Public methods

        public static void RemoveConnection(UserProfile profile, ConnectionProfileDTO connection)
        {
            if (profile == null)
                return;

            var upId = SaveProfile(profile);

            if (connection.Id.HasValue)
            {
                DeleteSingleConnection(upId, connection.Id.Value);
            }
        }

        public static void AddUpdateConnection(UserProfile profile, ConnectionProfileDTO connection)
        {
            if (profile == null)
                return;

            var upId = SaveProfile(profile);

            if (connection.Id.HasValue)
            {
                DeleteSingleConnection(upId, connection.Id.Value);
            }

            InsertConnection(profile, connection);
        }

        public static void UpdateProfileConnections(UserProfile profile)
        {
            profile.Connections = LoadConnections(profile);

            //LinkConnectionsToProfile(profile, connections);
        }


        /// <summary>
        ///     Adds to profile new connections from given list
        /// </summary>
        /// <param name="profile"></param>
        /// <param name="connections"></param>
        public static void LinkConnectionsToProfile(UserProfile profile, IEnumerable<ConnectionProfileDTO> connections)
        {
            if (profile.Connections != null)
            {
                foreach (var connection in profile.Connections)
                {
                    // we only add unique connections, not to have duplicates in list
                    if (!profile.Connections.Exists(con => con.Equals(connection)))
                    {
                        profile.Connections.Add(new VCConnection(connection));
                    }
                }
            }
            
        }

        /// <summary>
        ///     Overwrites connections in DB with connections in profile
        /// </summary>
        /// <param name="profile"></param>
        public static void Save(UserProfile profile)
        {
            // save profile itself
            var upId = SaveProfile(profile);

            // save connections

            //clear connections first
            DeleteConnectionsById(upId);

            // save connections
            foreach (var connection in profile.Connections)
            {
                InsertConnection(profile, connection);
            }
        }


        public static UserProfile FindById(string userOpenId)
        {
            using (var profiles = new Profiles())
            {
                var query = from up in profiles.UserProfile
                            where up.UserOpenId == userOpenId
                            select up;

                if (query.Count() > 0)
                {
                    var up = UnpackUserProfile(query.First());

                    up.Connections = LoadConnections(up);

                    return up;
                }
                return null;
            }
        }

        public static UserProfile FindByIdOrCreate(string userOpenId, string friendlyUserId)
        {
            var profile = FindById(userOpenId);

            if (profile == null)
            {
                profile = new UserProfile { UserOpenId = userOpenId, FriendlyOpenId = friendlyUserId };
            }
            else
            {
                profile.FriendlyOpenId = friendlyUserId;
            }

            return profile;
        }


        #endregion

        #region Private methods
        private static UserProfile GetProfileWithoutConnectionsById(string userOpenId)
        {
            using (var profiles = new Profiles())
            {
                var query = from up in profiles.UserProfile
                            where up.UserOpenId == userOpenId
                            select up;

                if (query.Count() > 0)
                {
                    var up = UnpackUserProfile(query.First());

                    return up;
                }
                return null;
            }
        }

        private static UserProfile UnpackUserProfile(Models.UserProfile up)
        {
            return new UserProfile { UserOpenId = up.UserOpenId, Id = up.Id };
        }

        private static ConnectionProfileDTO UnpackConnection(Models.ConnectionProfile conn)
        {
            return new ConnectionProfileDTO
                       {
                           Host = conn.Host,
                           IsLocalServer = conn.IsLocalConnection,
                           Password = conn.Password,
                           Port = conn.Port,
                           UseCompression = conn.UseCompression,
                           Username = conn.Username,
                           Id = conn.Id
                       };
        }

        private static List<ConnectionProfileDTO> LoadConnections(UserProfile profile)
        {
            var list = new List<ConnectionProfileDTO>();

            using (var context = new Profiles())
            {
                var query = from conn in context.ConnectionProfile
                            where conn.UserProfileId == profile.Id
                            select conn;

                list.AddRange(query.Select(UnpackConnection));
            }

            list = Decrypt(profile, list);

            return list;
        }

        private static void DeleteConnectionsById(int profileId)
        {
            using (var context = new Profiles())
            {
                context.ExecuteStoreCommand(@"DELETE FROM ConnectionProfile WHERE [UserProfileId]={0}", profileId);
            }
        }

        private static void DeleteSingleConnection(int profileId, int id)
        {
            using (var context = new Profiles())
            {
                context.ExecuteStoreCommand(@"DELETE FROM ConnectionProfile WHERE [UserProfileId]={0} AND [Id]={1}", profileId, id);
            }
        }

        private static void InsertConnection(UserProfile profile, ConnectionProfileDTO connection)
        {
            EncryptConnection(connection, profile.UserOpenId);

            using (var context = new Profiles())
            {
                var connectionProfile = new ConnectionProfile
                {
                    Host = connection.Host,
                    IsLocalConnection = connection.IsLocalServer,
                    Password = connection.Password,
                    Port = connection.Port,
                    UseCompression = connection.UseCompression,
                    Username = connection.Username,
                    UserProfileId = profile.Id
                };

                context.ConnectionProfile.AddObject(connectionProfile);
                context.SaveChanges();

                connection.Id = connectionProfile.Id;
            }
        }

        private static void EncryptConnection(ConnectionProfileDTO conn, string iv)
        {
            conn.Host = EncryptionLogic.Encrypt(conn.Host, iv);
            conn.Password = EncryptionLogic.Encrypt(conn.Password, iv);
        }

        /// <summary>
        ///     Returns list of decrypted connections for given profile and connections
        /// </summary>
        /// <param name="profile"></param>
        /// <param name="connections"></param>
        /// <returns></returns>
        private static List<ConnectionProfileDTO> Decrypt(UserProfile profile, IEnumerable<ConnectionProfileDTO> connections)
        {
            var list = new List<ConnectionProfileDTO>();
            foreach (var connection in connections)
            {
                var decrypted = connection.GetCopy();
                decrypted.Host = EncryptionLogic.Decrypt(connection.Host, profile.UserOpenId);
                decrypted.Password = EncryptionLogic.Decrypt(connection.Password, profile.UserOpenId);

                list.Add(decrypted);
            }

            return list;
        }

        private static int SaveProfile(UserProfile profile)
        {
            var existingProfile = GetProfileWithoutConnectionsById(profile.UserOpenId);

            // should create new profile or update existing one
            if (existingProfile == null)
            {
                using (var context = new Profiles())
                {
                    var up = new Models.UserProfile { UserOpenId = profile.UserOpenId };

                    context.AddToUserProfile(up);
                    context.SaveChanges();

                    profile.Id = up.Id;

                    return profile.Id;
                }
            }

            return existingProfile.Id;
        }

        #endregion

    }
}